


#include "vcd-rdp.h"
#include "vcd-common-api/vcd-log.h"
#include <errno.h>
#include "vcd-common-api/vcd-vm-rdp-connect.h"
#include "vcd-common-api/vcd-download.h"
#include "vcd-common-api/vcd-ping.h"
#include "vcd-common-api/vcd-util.h"
#include "vcd-common-api/vcd-json-parser.h"
#include "vcd-common-api/vcd-common.h"
#include "vcd-common-api/vcd-send-heartbeat.h"
#include "vcd-linux-ui/vcd-message-dialog.h"
#include "vcd-error.h"

#include "vcd-common-api/vcd-conf/vcd-conf.h"

#ifdef GDK_WINDOWING_X11
#include <X11/Xlib.h>
#include <gdk/gdkx.h>
#endif
#ifdef GDK_WINDOWING_WIN32
#include <gdk/gdkwin32.h>
#endif

extern vm_info connect_data;

extern void spicy_invalidate_win_spice(char *msg);
gboolean vcd_rdp_get_guest_ip_func(gpointer data);
static gboolean check_host(const char *host);
static void stop_rdp_process(void);
static void _vcd_rdp_start(gpointer spice_window, gpointer scd);

typedef struct _vcd_rdp_ctx{
    char guest_ip[100];
    guintptr xid;
    gint     port;
    gpointer scd;
    GThread *thrdp;
    // use on win32
    // 关闭spice时, 需要关闭rdp进程
    gpointer processId;
}vcd_rdp_ctx;

vcd_rdp_ctx g_ctx = {0};

void vcd_rdp_start(gpointer spice_window, gpointer priv){

    if (g_ctx.thrdp) {
        M_DEBUG("stream already running do nothing !!!!!!!");
        return;
    }

    spice_connect_data *s = (spice_connect_data*)priv;

#if 1
    _vcd_rdp_start(spice_window, priv);
#else
    if (strlen(connect_data.guest_ip) > 0) {
        _vcd_rdp_start(spice_window, priv);
    } else {
        vcd_vm_rdp_connect_get_guest_ip(vcd_rdp_get_guest_ip_func, spice_window);
    }else{
    }
#endif
}

gboolean vcd_rdp_is_running(){

    return g_ctx.thrdp ? TRUE : FALSE;
}

void vcd_rdp_stop(){
    if (vcd_rdp_is_running())
        stop_rdp_process();
}

static gboolean _start_rdp_use_system(gpointer arg0)
{
    vcd_rdp_ctx *ctx = (vcd_rdp_ctx*)arg0;
    spice_connect_data * scd = ctx->scd;

    guintptr xid      = ctx->xid;
    gint port         = ctx->port;
    char* guest_ip    = ctx->guest_ip;
    char* exe_path    = scd->rdp_stream_path ? scd->rdp_stream_path : VERYSTREAM_BIN;
    char* guest_name  = scd->guest_name;
    char* guest_pass  = scd->guest_pass;
    char* extend_cmd  = scd->streamExtend;
    char* extra_scale = scd->extra_scale;
    gboolean use_pass = scd->use_guest_user_pass;

    // 获取主屏幕的宽高
    GdkScreen *screen = gdk_screen_get_default();
    GdkMonitor *monitor = gdk_display_get_primary_monitor(gdk_display_get_default());

    // 多个屏幕的总宽高
    //int w = gdk_screen_get_width(screen);
    //int h = gdk_screen_get_height(screen);

    // 工作区域宽高,不带状态栏
    //GdkRectangle workarea;
    //gdk_monitor_get_workarea(monitor,&workarea);

    // 获取缩放倍数,只有整数
    int scale = gdk_monitor_get_scale_factor(monitor);

    // 获取主屏幕宽高
    GdkRectangle geometry;
    gdk_monitor_get_geometry(monitor,&geometry);


    // 高分屏缩放
    // 100% — 每英寸 96  像素
    // 125% — 每英寸 120 像素
    // 150% — 每英寸 144 像素
    // 200% — 每英寸 192 像素
    #define DEFAULT_DPI 96

    double rl = gdk_screen_get_resolution(screen);
    double s = rl / DEFAULT_DPI;

    #if 0
    int w = geometry.width / s;
    int h = geometry.height / s;
    #else
    int w = geometry.width * scale;
    int h = geometry.height * scale;
    #endif

    M_INFO("screen geometry:[%d x %d], wxh:[%d x %d], dpi:%f, scale:%d", geometry.width, geometry.height, w, h, s, scale);

    char cmd[2000] = {0};
    sprintf(cmd, "%s /v:%s:%d "
                 "/parent-window:0x%lx "
                 "/rfx "
                 "/rfx-mode:video "
                 "/network:auto "
                 "/video "
                 "/gfx:AVC444 "
                 "/gfx-h264:AVC444 "
                 "/cert-ignore "
                 "-toggle-fullscreen "
                 "+auto-reconnect "
                 "-sec-nla "
                 "/size:%dx%d "
                 //"/gdi:hw "
                 //"/scale:%s "
                 //"/span "
                 //"/f +window-drag "
                 //"+gfx-progressive "
                 //"-fonts "
                 //"/disp "
                 //"/workarea "
                 //"+menu-anims "
                 //"-themes "
                 // "/echo "
                 // "+fipsmode "
                 // "/dynamic-resolution "
                 // "/u:root "
                 // "/p:123 "
                 // "-decorations "
                 // "/geometry "
                 // "+gestures "
                 // "/smartcard-logon "
                 // "/size:1920x1080 "
                  //"+smart-sizing "
                 // "+window-drag "
                 , exe_path
                 , guest_ip
                 , port
                 , xid
                 , w
                 , h
                 //, extra_scale
                 );

    // add windows guest user name and password
    if (use_pass) {
        char extra[100] = {0};
        sprintf(extra, "/u:%s /p:%s ", guest_name, guest_pass);
        strcat(cmd, extra);
    }
    // add audio
    if (scd->use_audio) {
        char extra[100] = {0};
#if defined (__WIN32__)
        // 0x0001 0xA106
        //sprintf(extra, "%s ", "/sound:format:0xA106,quality:dynamic,channel:2,rate:44100,sys:winmm,latency:0");
        sprintf(extra, "%s ", "/sound:format:0xA106,quality:dynamic");
#elif defined(__linux__)
        sprintf(extra, "%s ", "/sound:format:0xA106,quality:dynamic");
#else

#endif
        strcat(cmd, extra);
    }

    // add local_disk_redir
    if(scd->use_local_disk_redirect){
        char extra[100] = {0};
        sprintf(extra, "%s ", "+home-drive");
        strcat(cmd, extra);
    }

    // add /ats-auth
    if (scd->gw) {
        strcat(cmd, "/ats-auth:");
        strcat(cmd, scd->gw);
        strcat(cmd, " ");
    }

    // add /gdi:
    if (scd->gdi && (scd->image_quality != 1))
        strcat(cmd, " /gdi:hw ");
    else
        strcat(cmd, " /gdi:sw ");

    if (extend_cmd)
        strcat(cmd, extend_cmd);

    if(scd->gdi && scd->image_quality == 1){
        M_INFO("image_quality == 1,not use hw");
    }

    M_DEBUG("cmd:%s", cmd);
    char* message = NULL;


#if defined (__linux__)
    int res = system(cmd);
    M_DEBUG("verystream system ret:%d", res);
    //system会阻塞住整个线程(不管xfreerdp是否执行成功)，如果exit_logined_window里面kill xfreerdp之后system会直接返回0
    if (res == 33536 || res == 8192 || res == 2 || res == -1 || res == 139 || res == 11776 || 34048) {
        //33536:虚拟机开启了防火墙  8192:待测
        //有可能下发的虚拟机ip是错的(虚拟机两块网卡ip获取的是另一个，到网关是通的)
        //有可能是虚拟机的端口不通
        //-1 139虚拟机hw参数不可用
        //11776 串流参数不全(scale:没了)
        //34048 虚拟机网络不通
        message = "虚拟机配置有误，verystream连接失败！ ";
    } else if (res == 32512) {
        message = "verystream未安装";
    } else if (res == 1280) {
        message = "有其他用户登录";
    }

    // system() return value
    // 9      killall -9
    // -1     killall -9 when not login windows
    // 32512  No such file or directory
    // 3072   The disconnection was initiated by the user logging off their session on the server
    // 33536  Network disconnect! ?
    //
    //
    // GetExitCodeProcess() exitCode
    // 0x20014 账户/密码错误   (0x20014 == 131092)
    //
    //
    // 查看错误码方法
    // libfreerdp/core/nla.c   nla_recv_pdu()
#elif defined (__WIN32__)

        STARTUPINFO si = { sizeof(STARTUPINFO) };
        PROCESS_INFORMATION pi;
        if (!CreateProcess(NULL,
            cmd,
            NULL,
            NULL,
            TRUE,
            0,              // No creation flags
            NULL,
            NULL,
            &si,
            &pi)){
            M_ERROR("[win32] CreateProcess failed");
        } else {
            M_INFO("[win32] verystream CreateProcess success");

            WaitForSingleObject(pi.hProcess,(DWORD)-1);

            // FreeRDP/include/freerdp/error.h
            DWORD exitCode = 0;
            if (GetExitCodeProcess(pi.hProcess, &exitCode))
                M_DEBUG("[verystream] exitcode:0x%X",exitCode);

            CloseHandle(pi.hProcess);
            CloseHandle(pi.hThread);

            if (0x20014 == exitCode) { // ERRCONNECT_LOGON_FAILURE
                LOG_ERROR("[win32] verystream account or password error!!!");
                message = "用户名或密码错误";
            } else if(0x10005 == exitCode) { // ERRINFO_DISCONNECTED_BY_OTHER_CONNECTION
                LOG_ERROR("[win32] verystream disconnected by other connection!!!");
                message = "有其他用户登录";
            } else if (0x2000D == exitCode || 0x2000C == exitCode) {//虚拟机端口不通或者管理平台串流密码设置错误
                LOG_ERROR("[win32] verystream gatewag password check failed!!!");
                message = "串流配置有误或网络端口不可达";
            } else if (0x1 == exitCode) {//虚拟机端口不通或者管理平台串流密码设置错误
                LOG_ERROR("0x1");
                message = "串流参数有误";
            }

            M_INFO("[win32] verystream process end exitCode：0x%x",exitCode);
            #ifdef USE_FREERDP_ERROR
            M_INFO("[win32] verystream exitcode name:%s info:%s  category:%s",freerdp_get_last_error_name(exitCode),freerdp_get_last_error_string(exitCode),freerdp_get_last_error_category(exitCode));
            #endif
        }
#else
        M_DEBUG("unknow define");
#endif

    ctx->thrdp = NULL;

    return FALSE;
}

static void _vcd_rdp_start(gpointer spice_window, gpointer scd) {

    if (!scd) return;

    spice_connect_data *s = (spice_connect_data *)scd;

    if (!s->use_rdp) {
        M_ERROR("use_stream == FALSE, not start stream");
        return;
    }

    if (vcd_rdp_is_running()) {

        LOG_ERROR("verystream already running, not start stream");
        return;
    }

    const char* guest_ip   = s->rdp_host;
    const char* guest_port = s->rdp_port;
    gint port = atoi(guest_port);

    if (!check_host(guest_ip)) {
        LOG_ERROR("gateway ip error");
        //spicy_invalidate_win_spice("网关ip错误");
        return;
    }

    const char* ip = vcd_common_resolve_domain(guest_ip);
    if (!vcd_ping_is_port_open(ip, (guint)port)) {
        LOG_ERROR("try to connect guest port failed host:%s, ip:%s, port:%d", guest_ip, ip, port);
        //spicy_invalidate_win_spice("检测网关ip以及端口连接失败");
        return;
    }

    // get xid
    GtkWidget* spiceWindow = (GtkWidget*)spice_window;
    if (!spiceWindow) {
        LOG_ERROR("win->window == NULL return");
        return;
    }

    GdkWindow* gdkWindow = gtk_widget_get_window(spiceWindow);
    if (!gdkWindow || !gdk_window_ensure_native(gdkWindow)) {
        LOG_ERROR("native window error");
        return;
    }

    guintptr xid = 0;
    #if defined GDK_WINDOWING_X11
    xid = GDK_WINDOW_XID(gdkWindow);
    #elif defined GDK_WINDOWING_WIN32
    xid = (guintptr)GDK_WINDOW_HWND(gdkWindow);
    #elif defined GDK_WINDOWING_QUARTZ
    #else
    #endif

    // disable-inputs
    g_object_set(spiceWindow, "disable-inputs", 1, NULL);

    vcd_rdp_ctx *ctx = &g_ctx;
    memset(ctx, 0, sizeof(vcd_rdp_ctx));

    sprintf(ctx->guest_ip, "%s", guest_ip);
    ctx->xid = xid;
    ctx->port = port;
    ctx->scd = s;

    ctx->thrdp = g_thread_new("stream_conn_task", (GThreadFunc)_start_rdp_use_system, (gpointer)ctx);
}

gboolean vcd_rdp_get_guest_ip_func(gpointer data)
{
    g_return_val_if_fail(data != NULL, FALSE);

    network_response *res = (network_response*)data;
    if (res->memory == NULL)
        goto end;

    M_DEBUG("stream res->memory:%s",res->memory);
    char guest_ip[100] = {0};
    if (json_parser_get_rdp_guest_ip(res->memory, guest_ip) != 0) {
        M_DEBUG("json_parser_get_stream_guest_ip error");
    }else{
       //_vcd_rdp_start(guest_ip, res->data_priv);
    }

end:
    network_response_free(&res);
    return FALSE;
}

static gboolean check_host(const char* host){

    if (!host)
        return FALSE;

    if (strlen(host)==0)
        return FALSE;

    return TRUE;
}

static void stop_rdp_process(){
    M_DEBUG("stop stream process");
    if (g_ctx.thrdp) {
        #ifdef __linux__
        char cmd[50] = {0};
        sprintf(cmd,"killall %s",VERYSTREAM_BIN);
        system(cmd);
        M_DEBUG(cmd);
        #endif

        #ifdef __WIN32__
        //if(g_ctx.processId){
        //    CloseHandle(g_ctx.processId);
        //    g_ctx.processId = 0;
        //}

//        M_DEBUG("程序正在推出,检查子进程是否退出...");
//        // verystream.exe
        if (check_is_process_exist("verystream.exe", TRUE)){
        //    M_DEBUG("[verystream.exe] 进程已经全部退出");
        }
        #endif
    }
}

static gboolean set_cmdline(char* cmd,spice_connect_data *scd){

    char* guest_ip    = scd->rdp_host;
    char* guest_port  = scd->rdp_port;
    char* exe_path    = scd->rdp_stream_path ? scd->rdp_stream_path : VERYSTREAM_BIN;
    char* guest_name  = scd->guest_name;
    char* guest_pass  = scd->guest_pass;
    char* extend_cmd  = scd->streamExtend;
    char* extra_scale = scd->extra_scale;
    gboolean use_pass = scd->use_guest_user_pass;

    sprintf(cmd, "%s /v:%s:%d ", exe_path, guest_ip, atoi(guest_port) );
    strcat(cmd," /rfx /rfx-mode:video /video /gfx:AVC444 /gfx-h264:AVC444 /cert-ignore -toggle-fullscreen +auto-reconnect -sec-nla ");

    // add float bar
    strcat(cmd, " /t:verystream /floatbar:sticky:off,show:always /f ");

    // add windows guest user name and password
    if (use_pass) {
        char extra[100] = {0};
#ifdef __linux__
        sprintf(extra, "/u:%s /p:'%s' ", guest_name, guest_pass);
#else
        sprintf(extra, "/u:%s /p:%s ", guest_name, guest_pass);
#endif
        strcat(cmd, extra);
    }
    // add audio
    if (scd->use_audio) {
        strcat(cmd, " /sound:format:0xA106,quality:dynamic ");
    }
    // add local_disk_redir
    if(scd->use_local_disk_redirect){
        strcat(cmd, " +home-drive ");
    }
    // add /ats-auth
    if (scd->gw) {
        strcat(cmd, " /ats-auth:");
        strcat(cmd, scd->gw);
        strcat(cmd, " ");
    }
    // add /gdi:
    if (scd->gdi && (scd->image_quality != 1))
        strcat(cmd, " /gdi:hw ");
    else
        strcat(cmd, " /gdi:sw ");

    // add /scale:100/140/180
//    if(extra_scale){
//        strcat(cmd, " /scale:");
//        strcat(cmd, extra_scale);
//        strcat(cmd, " ");
//    }

    if (scd->gdi && scd->image_quality == 1) {
        M_INFO("image_quality == 1, not use hw");
    }

    if(scd->use_usb_redir || scd->protocol_type == PROTOCOL_RDP || scd->protocol_type == PROTOCOL_STREAM){
        strcat(cmd," /spice-host:"); strcat(cmd,scd->host);
        strcat(cmd," /spice-port:"); strcat(cmd,scd->port);
        strcat(cmd," /spice-passwd:"); strcat(cmd,scd->password);
        strcat(cmd," /spice-proxy:"); strcat(cmd,scd->proxy);

        strcat(cmd," /spice-usb:");
        strcat(cmd,"\"");
        #if __WIN32__
        if(scd->install_path){
            strcat(cmd,scd->install_path);
            strcat(cmd,"\\bin\\");
        }
        #elif __linux__
        //strcat(cmd,"/usr/bin/");
        #else
        #endif
        strcat(cmd, VCD_USB_BIN);
        strcat(cmd, "\" ");

        if (scd->use_usb_redir)
            strcat(cmd," +enable-spice-usb-redir ");
        //if(scd->protocol_type == PROTOCOL_STREAM){
        if ((scd->gw && !strstr(scd->gw,"23389")) && (scd->protocol_type == PROTOCOL_STREAM)) {
            strcat(cmd," +verystream-server ");
            strcat(cmd," -heartbeat ");
            strcat(cmd, " /gdi:hw ");

            char tmp[100] = {0};
            sprintf(tmp," /rate:%d ", scd->image_quality);
            strcat(cmd, tmp);
        }
    }

    // add extend cmd
    if (extend_cmd)
        strcat(cmd, extend_cmd);

    return TRUE;
}

static gboolean check_host_and_port(char* host,int port){

    if (!check_host(host)) {
        LOG_ERROR("网关ip错误");
        message_box("网关ip错误");
        return FALSE;
    }

    const char* ip = vcd_common_resolve_domain(host);
    if (!vcd_ping_is_port_open(ip, (guint)port)) {
        LOG_ERROR("connect failed host:%s, ip:%s, port:%d", host, ip, port);
        message_box("检测网关ip以及端口连接失败");
        return FALSE;
    }

    return TRUE;
}

void vcd_rdp_start_sync(gpointer priv){

    // test
    //char cmd_test[2000] = {0};#include "vcd-common-api/vcd-util.h"

    //update_spice_info_sync(cmd_test,0);
    //return;

    spice_connect_data *scd = (spice_connect_data*)priv;

    char* guest_ip    = scd->rdp_host;
    char* guest_port  = scd->rdp_port;

    if(!check_host_and_port(guest_ip,atoi(guest_port))){
        return;
    }

    char* message = NULL;
    gboolean isReconnect = FALSE;

    while(1){

        if (isReconnect) {
            update_spice_info_sync(scd);
            isReconnect = FALSE;
        }

        char cmd[2000] = {0};
        set_cmdline(cmd,scd);

        M_DEBUG("cmd:%s", cmd);
        int status = 0;

        #if defined (__linux__)

        int res = system(cmd);

        char *exit_name = NULL;
        if (WIFEXITED(res)) {
            //verystream exit success
            status = WEXITSTATUS(res);
            M_DEBUG("status:%d", status);

            exit_name   = freerdp_get_status_name(status);
            message     = freerdp_get_status_string(status);
            isReconnect = freerdp_get_status_reconnect_status(status);

        } else {
            //仅ret=-1的时候出现，退出是正常的，所以不做处理
        }
        M_INFO("verystream process end ret:%d WEXITSTATUS:0x%x exit_name:%s message:%s",
               res,status, exit_name ? exit_name : " ", message ? message : " ");

//        char *quit_logining_window_cmd = "kill -9 $(ps -ef|grep spicy|grep logining-window|awk '{print $2}')";
//        system(quit_logining_window_cmd);

        // 查看错误码方法
        // libfreerdp/core/nla.c   nla_recv_pdu()
        #elif defined (__WIN32__)
            STARTUPINFO si = { sizeof(STARTUPINFO) };
            PROCESS_INFORMATION pi;
            if (!CreateProcess(NULL,
                cmd,
                NULL,
                NULL,
                TRUE,
                0,              // No creation flags
                NULL,
                NULL,
                &si,
                &pi)){
                M_ERROR("[win32] CreateProcess failed");
            } else {
                M_INFO("[win32] verystream CreateProcess success");

                WaitForSingleObject(pi.hProcess,(DWORD)-1);

                M_INFO("[win32] verystream CreateProcess end");

                // FreeRDP/include/freerdp/error.h
                DWORD exitCode = 0;
                if (GetExitCodeProcess(pi.hProcess, &exitCode))
                    M_DEBUG("[verystream] exitcode:0x%X",exitCode);

                M_INFO("[win32] verystream exitCode:%d",exitCode);
                CloseHandle(pi.hProcess);
                CloseHandle(pi.hThread);

                message     = freerdp_get_status_string(exitCode);
                isReconnect = freerdp_get_status_reconnect_status(exitCode);

                M_INFO("[win32] verystream process end exitCode：0x%x message:%s",exitCode,message ? message : " ");
                #ifdef USE_FREERDP_ERROR
                M_INFO("[win32] verystream exitcode name:%s info:%s  category:%s",freerdp_get_last_error_name(exitCode),freerdp_get_last_error_string(exitCode),freerdp_get_last_error_category(exitCode));
                #endif
            }
        #else
            M_DEBUG("unknow define");
        #endif

        if (message)
            message_box(message);

        if(!isReconnect)
            break;
    }

    //send disconnect signal to gateway
    tell_gateway_client_disconnect();
    //join the process to send disconnect
    stop_download_thread();
}
